<template>
  <div class="cropper">
    <image
      v-if="!showCropper"
      :src="imageSrc"
      class="cropper-avatar"
      mode="aspectFill"
      @click="uploadSelected"
      :style="imageStyle"
    ></image>

    <template v-if="showCropper">
      <div class="cropper-main">
        <image
          :src="cropperImage"
          class="cropper-image"
          mode="aspectFit"
          :style="imageStyle"
        ></image>
        <!-- 画布 -->
        <canvas
          canvas-id="my-canvas"
          id="my-canvas"
          class="cropper-my-canvas"
          disable-scroll="false"
          :style="{ top: cropperTop, height: cropperHeight }"
        ></canvas>
        <canvas
          canvas-id="oper-canvas"
          id="oper-canvas"
          class="cropper-oper-canvas"
          :style="{ top: cropperTop, height: cropperHeight }"
          disable-scroll="false"
          @touchstart="fStart"
          @touchmove="fMove"
          @touchend="fEnd"
        ></canvas>
        <!-- 底部按钮 -->
        <div class="cropper-btn">
          <div class="item" @click="uploadSelected">重新选择图片</div>
          <div class="item" @click="close">取消</div>
          <div class="item" @click="createImage">确定生成</div>
        </div>
      </div>
    </template>
  </div>
</template>
  <script lang="ts">
export default {
  name: "cropper",
};
</script>
<script lang="ts" setup>
import { ref, getCurrentInstance, onMounted } from "vue";
const {
  proxy,
  proxy: { $utils },
} = getCurrentInstance() as any;

const props = withDefaults(
  defineProps<{
    imageSrc?: string;
    imageStyle?: string;
    selWidth?: number;
    selHeight?: number;
    minScale?: number;
    maxScale?: number;
    index?: number;
  }>(),
  {
    imageSrc: "",
    imageStyle: "",
    selWidth: 200,
    selHeight: 200,
    index: 0,
  }
);

const emit = defineEmits(["change"]);

const showCropper = ref<boolean>(false);

const bottomBtnHeight = ref<number>(70);

// 画布顶部距离
const cropperTop = ref<string>("0");

// 画布高度
const cropperHeight = ref<string>("0px");

let sysInfo = uni.getSystemInfoSync();

const toInit = () => {
  cropperHeight.value = sysInfo.windowHeight - bottomBtnHeight.value + "px";
};

const selStyle = ref<any>();

const useWidth = ref<number>(200);

const useHeight = ref<number>(200);

const mnScale = ref<number>(props.minScale || 0.3);

const mxScale = ref<number>(props.maxScale || 4);

const posWidth = ref<number>(0);

const posHeight = ref<number>(0);

const exportWidth = ref<number>(0);

const exportHeight = ref<number>(0);

const postFirst = ref<any>({
  left: 0,
  top: 0,
  width: 0,
  height: 0,
  posWidth: posWidth.value,
  posHeight: posHeight.value,
});

// 图片
const cropperImage = ref<string>(props.imageSrc);

// 图片宽度
const cropperImageWidth = ref<number>(0);

// 图片高度
const cropperImageHeihgt = ref<number>(0);

const styleDisplay = ref<boolean>(true);

//比例缩放
const scaleSize = ref<number>(1);

const uploadSelected = () => {
  uni.chooseImage({
    count: 1,
    sizeType: ["original", "compressed"],
    sourceType: ["camera", "album"],
    success: (r) => {
      showCropper.value = true;
      cropperImage.value = cropperImage.value = r.tempFilePaths[0];
      uni.getImageInfo({
        src: cropperImage.value,
        success: (r) => {
          cropperImageWidth.value = r.width;
          cropperImageHeihgt.value = r.height;
          let style = selStyle.value || {};

          style.width = props.selWidth;
          style.height = props.selHeight;
          style.top =
            (sysInfo.windowHeight - style.height - bottomBtnHeight.value) / 2;
          style.left = (sysInfo.windowWidth - style.width) / 2;
          selStyle.value = style;

          fDrawInit(true);
        },
      });
    },
  });
};

const fDrawInit = (ini: boolean = false) => {
  let left = selStyle.value?.left || 0,
    top = selStyle.value?.top || 0,
    width = selStyle.value?.width || 0,
    height = selStyle.value?.height || 0,
    imgRadio = cropperImageWidth.value / cropperImageHeihgt.value;
  if (imgRadio < 1) {
    useWidth.value = props.selWidth;
    useHeight.value = useWidth.value / imgRadio;
  } else {
    useHeight.value = props.selHeight;
    useWidth.value = useHeight.value * imgRadio;
  }

  posWidth.value = (sysInfo.windowWidth - useWidth.value) / 2;
  posHeight.value =
    (sysInfo.windowHeight - useHeight.value - bottomBtnHeight.value) / 2;

  operCanvas.value.setFillStyle("rgba(0,0,0, 0.5)");
  operCanvas.value.fillRect(0, 0, sysInfo.windowWidth, top);
  operCanvas.value.fillRect(0, top, left, height);
  operCanvas.value.fillRect(
    0,
    top + height,
    sysInfo.windowWidth,
    sysInfo.windowHeight - height - bottomBtnHeight.value - top
  );
  operCanvas.value.fillRect(
    left + width,
    top,
    sysInfo.windowWidth - width - left,
    height
  );
  operCanvas.value.setLineWidth(1);
  operCanvas.value.setStrokeStyle("rgba(255, 255, 255,1)"); //细线的颜色
  operCanvas.value.strokeRect(left, top, width, height);

  operCanvas.value.setLineWidth(3);
  operCanvas.value.setStrokeStyle("rgba(255, 255, 255, 1)"); //粗线的颜色
  operCanvas.value.moveTo(left + 20, top);
  operCanvas.value.lineTo(left, top);
  operCanvas.value.lineTo(left, top + 20);
  operCanvas.value.moveTo(left + width - 20, top);
  operCanvas.value.lineTo(left + width, top);
  operCanvas.value.lineTo(left + width, top + 20);
  operCanvas.value.moveTo(left + 20, top + height);
  operCanvas.value.lineTo(left, top + height);
  operCanvas.value.lineTo(left, top + height - 20);
  operCanvas.value.moveTo(left + width - 20, top + height);
  operCanvas.value.lineTo(left + width, top + height);
  operCanvas.value.lineTo(left + width, top + height - 20);
  operCanvas.value.stroke();
  postFirst.value = {
    left: left,
    top: top,
    width: width,
    height: selStyle.value.width,
    posWidth: posWidth.value,
    posHeight: posHeight.value,
  };
  operCanvas.value.draw(false, () => {
    if (ini) {
      styleDisplay.value = true;
      myCanvas.value.setFillStyle("black");
      fDrawImage();
    }
  });
};

const drawTm = ref<number>(0);

const fDrawImage = () => {
  let tm_now = Date.now();
  if (tm_now - drawTm.value < 20) return;
  drawTm.value = tm_now;
  myCanvas.value.fillRect(
    0,
    0,
    sysInfo.windowWidth,
    sysInfo.windowHeight - bottomBtnHeight.value
  );
  //中心点坐标
  myCanvas.value.translate(
    posWidth.value + useWidth.value / 2,
    posHeight.value + useHeight.value / 2
  );
  //比例缩放
  myCanvas.value.scale(scaleSize.value, scaleSize.value);
  myCanvas.value.drawImage(
    cropperImage.value,
    -useWidth.value / 2,
    -useHeight.value / 2,
    useWidth.value,
    useHeight.value
  );
  myCanvas.value.draw(false);
};

const touch0 = ref<any>();
const touch1 = ref<any>();
const fgDistance = ref<any>();

const fStart = (e: any) => {
  let touches = e.touches;

  touch0.value = touches[0];
  touch1.value = touches[1];

  if (touch1.value) {
    let x = touch1.value.x - touch0.value.x,
      y = touch1.value.y - touch0.value.y;
    fgDistance.value = Math.sqrt(x * x + y * y);
  }
};

const rotateDeg = ref<number>(0);

const fMove = (e: any) => {
  let touches = e.touches,
    _touch0 = touches[0],
    _touch1 = touches[1];

  if (_touch1) {
    let x = _touch1.x - _touch0.x,
      y = _touch1.y - _touch0.y,
      _fgDistance = Math.sqrt(x * x + y * y),
      _scaleSize = 0.005 * (_fgDistance - fgDistance.value),
      beScaleSize = _scaleSize + scaleSize.value;

    do {
      if (beScaleSize < mnScale.value) break;
      if (beScaleSize > mxScale.value) break;
      scaleSize.value = beScaleSize;
    } while (0);
    fgDistance.value = _fgDistance;

    if (_touch1.x !== _touch0.x) {
      x = (touch1.value.y - touch0.value.y) / (touch1.value.x - touch0.value.x);
      y = (_touch1.y - _touch0.y) / (_touch1.x - _touch0.x);
      rotateDeg.value += (Math.atan((y - x) / (1 + x * y)) * 180) / Math.PI;
      touch0.value = _touch0;
      touch1.value = _touch1;
    }
    fDrawImage();
  } else if (touch0.value) {
    let x = _touch0.x - touch0.value.x,
      y = _touch0.y - touch0.value.y,
      beX = posWidth.value + x,
      beY = posHeight.value + y;
    if (Math.abs(x) < 100) posWidth.value = beX;
    if (Math.abs(y) < 100) posHeight.value = beY;
    touch0.value = _touch0;
    fDrawImage();
  }
};

const fEnd = async (e: any) => {
  let touches = e.touches,
    _touch0 = touches[0],
    _touch1 = touches[1];
  if (scaleSize.value < 1) {
    let style = selStyle.value;
    let imgRadio = cropperImageWidth.value / cropperImageHeihgt.value;
    //高长宽短
    if (imgRadio < 1 && scaleSize.value * useWidth.value < style.width) {
      posWidth.value = style.left;
      scaleSize.value = 1;
      setTimeout(function () {
        fDrawImage();
      }, 100);
    } else if (scaleSize.value * useHeight.value < style.width) {
      //高短宽长
      posHeight.value = style.top;
      scaleSize.value = 1;
      setTimeout(function () {
        fDrawImage();
      }, 100);
    }
  } else if (scaleSize.value == 1) {
    let endWidth = posWidth.value - postFirst.value.posWidth,
      firstWidth = postFirst.value.left - postFirst.value.posWidth;
    let endHeight = posHeight.value - postFirst.value.posHeight,
      firstHigth = postFirst.value.top - postFirst.value.posHeight;
    if (endWidth > 0 && posWidth.value > postFirst.value.left) {
      //右滑动过长
      posWidth.value = postFirst.value.left;
    } else if (endWidth < 0 && endWidth < firstWidth) {
      //左滑动过长
      posWidth.value = -postFirst.value.left + postFirst.value.posWidth * 2;
    }

    if (endHeight < 0 && posHeight.value < postFirst.value.top) {
      //上滑动过长
      posHeight.value = -postFirst.value.top + postFirst.value.posHeight * 2;
    } else if (endHeight > 0 && endHeight > firstHigth) {
      //下滑动过长
      posHeight.value = postFirst.value.top;
    }
    setTimeout(function () {
      fDrawImage();
    }, 100);
  }
  if (_touch0) {
    touch0.value = _touch0;
  } else {
    touch0.value = null;
    touch1.value = null;
  }
};

const createLoading = ref<boolean>(false);

const qlty = ref<number>(0.9);

// 确定生成
const createImage = () => {
  if (createLoading.value) return;
  createLoading.value = true;
  let style = selStyle.value,
    x = parseInt(style.left),
    y = parseInt(style.top),
    width = parseInt(style.width),
    height = parseInt(style.height),
    expWidth = width,
    expHeight = height;
  uni.canvasToTempFilePath(
    {
      x: x,
      y: y,
      width: width,
      height: height,
      destWidth: expWidth,
      destHeight: expHeight,
      canvasId: "my-canvas",
      fileType: "png",
      quality: qlty.value,
      success: (r) => {
        emit("change", {
          path: cropperImage.value,
          cropper: r.tempFilePath,
          index: props.index,
        });
        close();
      },
      fail: (r) => {
        $utils.errorMsg(r.errMsg);
      },
      complete: (r) => {
        createLoading.value = false;
      },
    },
    proxy
  );
};

// 画布
const operCanvas = ref<any>();

const myCanvas = ref<any>();

onMounted(() => {
  operCanvas.value = uni.createCanvasContext("oper-canvas", proxy);
  myCanvas.value = uni.createCanvasContext("my-canvas", proxy);
  toInit();
});

const close = () => {
  showCropper.value = false;
};
</script>
  
  <style scoped>
.cropper {
  display: flex;
}
.cropper-main {
}
.cropper-image {
  width: 100vw;
  height: 100vw;
}
.cropper-my-canvas {
  display: flex;
  position: fixed;
  left: 0;
  z-index: 100;
  width: 100%;
  background: #000000;
}
.cropper-oper-canvas {
  display: flex;
  position: fixed !important;
  left: 0;
  z-index: 101;
  width: 100%;
}
.cropper-btn {
  background-color: #000000;
  color: #ffffff;
  height: 71px;
  position: fixed;
  width: 100%;
  left: 0;
  bottom: 0;
  z-index: 200;
  display: flex;
  justify-content: space-around;
  align-items: center;
}

.cropper-btn .item {
  width: 160rpx;
  height: 80rpx;
  line-height: 80rpx;
  text-align: center;
  font-size: var(--base-size-default);
  color: #ffffff;
  z-index: 300;
}
</style>
  